home *** CD-ROM | disk | FTP | other *** search
/ BBS in a Box 3 / BBS in a box - Trilogy III.iso / Files / System7 tools / Frontier / Frontier SDK 2.1 / Toolkits / Menu Sharing Toolkit / menusharing.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-09  |  15.7 KB  |  662 lines  |  [TEXT/KAHL]

  1.    
  2. /*© Copyright 1992 UserLand Software, Inc.  All Rights Reserved.*/
  3.  
  4.  
  5. /*
  6. Version 2.0:
  7.  
  8. 1/22/92 DW: provide for dialog on receipt of a 'done' message
  9.     
  10.     1. Frontier sets the 'errs' parameter if a shared script ended in an
  11.     error. we set up the IAC globals so that the scriptcomplete handler
  12.     can get the error string and display it in a dialog.
  13.     
  14. 6/29/92 DW: fast menu sharing if Frontier 2.0 is present.
  15.  
  16.     1. this is possible because Frontier 2.0 installs system event handlers
  17.     for the most time-consuming of the menu sharing operations.
  18.     
  19. 8/13/92 DW: cleaner way of killing scripts.
  20.     
  21.     1. In CancelSharedScript, we now send a message to the menu sharing server
  22.     asking that the script be killed. But only if we're talking to a server that
  23.     supports fast messages.
  24. */
  25.  
  26.  
  27. #include <processes.h>
  28. #include <Menus.h>
  29. #include <GestaltEqu.h>
  30. #include <iac.h>
  31. #include "menusharing.h"
  32.  
  33.  
  34.  
  35. tyMSglobals MSglobals; /*Menu Sharing globals, all in one struct*/
  36.  
  37.  
  38.  
  39. static Boolean ServerSupportsFastMessages (void) {
  40.     
  41.     /*
  42.     return true if there's a system event handler registered to support the
  43.     get-menu-array message. Frontier 2.0 installs such a handler, other servers
  44.     (e.g. Frontier 1.0) don't.
  45.     */
  46.     
  47.     return (IAChandlerinstalled (MSglobals.serverid, 'gmry', true));
  48.     } /*ServerSupportsFastMessages*/
  49.     
  50.     
  51. static Boolean MSnewverb (OSType verbtoken, AppleEvent *event) {
  52.     
  53.     Boolean fl;
  54.     
  55.     if (ServerSupportsFastMessages ())
  56.         fl = IACnewsystemverb (MSglobals.serverid, verbtoken, event);
  57.     else 
  58.         fl = IACnewverb (MSglobals.serverid, MSglobals.serverid, verbtoken, event);
  59.         
  60.     return (fl);
  61.     } /*MSnewverb*/
  62.         
  63.  
  64. static pascal Boolean ProcessInForeground () {
  65.     
  66.     /*
  67.     return true if we're running in the foreground, false if we're in the
  68.     background.
  69.     */
  70.     
  71.     ProcessSerialNumber currentprocess, frontprocess;
  72.     Boolean fl;
  73.     
  74.     GetCurrentProcess (¤tprocess);
  75.     
  76.     GetFrontProcess (&frontprocess);
  77.     
  78.     SameProcess (¤tprocess, &frontprocess, &fl);
  79.     
  80.     return (fl);
  81.     } /*ProcessInForeground*/
  82.     
  83.     
  84. static Boolean ServerIsRunning (void) {
  85.     
  86.     /*
  87.     return true if the server application is running. 
  88.     */
  89.     
  90.     ProcessInfoRec info;
  91.     ProcessSerialNumber psn;
  92.     Str255 bsname;
  93.     FSSpec fss;
  94.     
  95.     info.processInfoLength = sizeof (info);
  96.     
  97.     info.processName = bsname; /*place to store process name*/
  98.     
  99.     info.processAppSpec = &fss; /*place to store process filespec*/
  100.     
  101.     psn.highLongOfPSN = kNoProcess;
  102.     
  103.     psn.lowLongOfPSN = kNoProcess;
  104.     
  105.     while (GetNextProcess (&psn) == noErr) {
  106.         
  107.          info.processInfoLength = sizeof (ProcessInfoRec);
  108.          
  109.         if (GetProcessInformation (&psn, &info) != noErr)
  110.             continue; /*keep going -- ignore error*/
  111.         
  112.         if (info.processSignature == MSglobals.serverid)
  113.             return (true);
  114.         } /*while*/
  115.     
  116.     return (false); /*loop completed, no server*/
  117.     } /*ServerIsRunning*/
  118.  
  119.  
  120. static short CountMenuArray (void) {
  121.     
  122.     /*
  123.     return the number of menus in the menu array.
  124.     */
  125.     
  126.     register hdlmenuarray hm = MSglobals.hsharedmenus;
  127.     
  128.     if (hm == nil)
  129.         return (0);
  130.     
  131.     return ((short) (GetHandleSize ((Handle) hm) / sizeof (tysharedmenurecord)));
  132.     } /*CountMenuArray*/
  133.  
  134.  
  135. static pascal Boolean InstallSharedMenus (short idmenuafter) {
  136.     
  137.     /*
  138.     insert all of the menus in the menuarray into the menu bar.  main 
  139.     menus are inserted in front of idmenuafter. if idmenuafter is zero, 
  140.     main (non-hierarchic) menus will be added to the right of all others.
  141.     */
  142.     
  143.     register hdlmenuarray hm = MSglobals.hsharedmenus;
  144.     register short i, ct;
  145.     tysharedmenurecord item;
  146.     
  147.     ct = CountMenuArray ();
  148.     
  149.     for (i = 0; i < ct; i++) {
  150.         
  151.         item = (**hm) [i];
  152.         
  153.         if (item.flhierarchic)
  154.             InsertMenu (item.hmenu, -1);
  155.         else
  156.             InsertMenu (item.hmenu, idmenuafter);
  157.         
  158.         (**hm) [i].flinserted = true; /*so we'll know it needs to be removed*/
  159.         } /*for*/
  160.     
  161.     return (true);
  162.     } /*InstallSharedMenus*/
  163.  
  164.  
  165. static short GetMenuHandles (void) {
  166.     
  167.     /*
  168.     loop through the menuarray, send an IAC message to the menu server requesting
  169.     that each MenuHandle be sent to us.
  170.     */
  171.     
  172.     register hdlmenuarray hm = MSglobals.hsharedmenus;
  173.     register short i, ct;
  174.     AppleEvent event, reply;
  175.     register short fl;
  176.     MenuHandle hmenu;
  177.     OSType binarytype;
  178.     
  179.     ct = CountMenuArray ();
  180.     
  181.     for (i = 0; i < ct; i++) {
  182.     
  183.         if (!MSnewverb ('gmhd', &event))
  184.             return (false);
  185.         
  186.         IACglobals.event = &event;
  187.         
  188.         if (!IACpushlongparam (MSglobals.clientid, 'menp'))
  189.             return (false);
  190.         
  191.         if (!IACpushshortparam (i, 'idix'))
  192.             return (false);
  193.         
  194.         if (!IACsendverb (IACglobals.event, &reply))
  195.             return (false);
  196.         
  197.         IACglobals.reply = &reply;
  198.         
  199.         IACglobals.event = &reply;
  200.         
  201.         fl = IACgetbinaryparam (keyDirectObject, (Handle *) &hmenu, &binarytype);
  202.         
  203.         IACdisposeverb (&reply);
  204.         
  205.         if (!fl)
  206.             return (false);
  207.         
  208.         (**hm) [i].hmenu = hmenu;
  209.         } /*for*/
  210.     
  211.     return (true);
  212.     } /*GetMenuHandles*/
  213.  
  214.  
  215. static pascal Boolean GetSharedMenus (short firstresource) {
  216.     
  217.     /*
  218.     call the menu server to get a menuarray, keyed off of our application id.
  219.     
  220.     firstresource is the starting id to be used for the menus; if there are 
  221.     n menus, their ids will range from firstresource to firstresource + n - 1.
  222.     */
  223.     
  224.     AppleEvent event, reply;
  225.     register short fl;
  226.     OSType binarytype;
  227.     
  228.     if (!MSnewverb ('gmry', &event))
  229.         return (false);
  230.         
  231.     IACglobals.event = &event;
  232.     
  233.     if (!IACpushlongparam (MSglobals.clientid, 'menp'))
  234.         return (false);
  235.     
  236.     if (!IACpushshortparam (firstresource, 'res1'))
  237.         return (false);
  238.         
  239.     if (!IACsendverb (&event, &reply))
  240.         return (false);
  241.         
  242.     IACglobals.event = &reply;
  243.     
  244.     fl = IACgetbinaryparam (keyDirectObject, (Handle *) &MSglobals.hsharedmenus, &binarytype);
  245.     
  246.     IACdisposeverb (&reply);
  247.     
  248.     if (!fl)
  249.         return (false);
  250.     
  251.     return (GetMenuHandles ());
  252.     } /*GetSharedMenus*/
  253.  
  254.  
  255. pascal Boolean DisposeSharedMenus (void) {
  256.     
  257.     /*
  258.     completely dispose of the menuarray and the menu handles it contains.
  259.     
  260.     10/10/91 DW: check for no shared menus before disposing, save code if 
  261.     its ever called from more than one place. also set the global handle to
  262.     nil after disposing and redraw the menu bar.
  263.     */
  264.     
  265.     register hdlmenuarray hm = MSglobals.hsharedmenus;
  266.     register short i;
  267.     register short ctmenus;
  268.     tysharedmenurecord item;
  269.     
  270.     if (hm == nil) /*no shared menus to toss*/
  271.         return (true);
  272.     
  273.     ctmenus = CountMenuArray ();
  274.     
  275.     for (i = 0; i < ctmenus; i++) {
  276.         
  277.         item = (**hm) [i];
  278.         
  279.         if (item.flinserted)
  280.             DeleteMenu (item.idmenu);
  281.         
  282.         DisposeMenu (item.hmenu);
  283.         } /*for*/
  284.     
  285.     DisposHandle ((Handle) hm);
  286.     
  287.     MSglobals.hsharedmenus = nil;
  288.     
  289.     DrawMenuBar ();
  290.     
  291.     return (true);
  292.     } /*DisposeSharedMenus*/
  293.  
  294.  
  295. pascal Boolean IsSharedMenu (short idmenu) {
  296.     
  297.     /*
  298.     return true if the indicated menu is one of the shared menus.
  299.     */
  300.     
  301.     register hdlmenuarray hm = MSglobals.hsharedmenus;
  302.     register short ct, i;
  303.     tysharedmenurecord item;
  304.     
  305.     ct = CountMenuArray ();
  306.     
  307.     for (i = 0; i < ct; i++) {
  308.         
  309.         item = (**hm) [i];
  310.         
  311.         if (item.idmenu == idmenu)
  312.             return (true);
  313.         } /*for*/
  314.         
  315.     return (false);
  316.     } /*IsSharedMenu*/
  317.  
  318.  
  319. pascal Boolean EnableSharedMenus (Boolean flenable) {
  320.     
  321.     /*
  322.     Enables or disables the the menus in the specified menu array.
  323.     
  324.     Always returns true.
  325.     */
  326.     
  327.     register hdlmenuarray hm = MSglobals.hsharedmenus;
  328.     register short i;
  329.     register short ctmenus;
  330.     register MenuHandle hmenu;
  331.     
  332.     ctmenus = CountMenuArray ();
  333.     
  334.     for (i = 0; i < ctmenus; i++) {
  335.         
  336.         hmenu = (**hm) [i].hmenu;
  337.         
  338.         if (flenable)
  339.             EnableItem (hmenu, 0);
  340.         else
  341.             DisableItem (hmenu, 0);
  342.         } /*for*/
  343.     
  344.     DrawMenuBar ();
  345.     
  346.     return (true);
  347.     } /*EnableSharedMenus*/
  348.  
  349.  
  350. pascal Boolean RunSharedMenuItem (short idmenu, short iditem) {
  351.      
  352.     /*
  353.     call the menu server to run the script linked to the indicated menu item.
  354.     
  355.     the script will execute asynchonously, after this call returns.
  356.     
  357.     SDK 2.0: if the server isn't running, remove the shared menus and return
  358.     false. this will only happen if the server has crashed without letting us
  359.     know that our menus are dirty.
  360.     */
  361.     
  362.     AppleEvent event, reply;
  363.     Boolean fl;
  364.     
  365.     if (!ServerIsRunning ()) {
  366.         
  367.         MSglobals.fldirtysharedmenus = true;
  368.         
  369.         return (false);
  370.         }
  371.     
  372.     if (!IACnewverb (MSglobals.serverid, MSglobals.serverid, 'runm', &event))
  373.         return (false);
  374.     
  375.     IACglobals.event = &event;
  376.     
  377.     if (!IACpushlongparam (MSglobals.clientid, 'menp'))
  378.         return (false);
  379.     
  380.     if (!IACpushshortparam (idmenu, 'mid '))
  381.         return (false);
  382.     
  383.     if (!IACpushshortparam (iditem, 'mitm'))
  384.         return (false);
  385.     
  386.     if (!IACsendverb (&event, &reply))
  387.         return (false);
  388.         
  389.     IACglobals.event = &reply;
  390.         
  391.     fl = IACgetlongparam (keyDirectObject, &MSglobals.idscript);
  392.     
  393.     IACdisposeverb (&reply);
  394.     
  395.     return (fl && (MSglobals.idscript != 0));
  396.     } /*RunSharedMenuItem*/
  397.  
  398.  
  399. pascal Boolean CheckSharedMenus (idinsertafter) short idinsertafter; {
  400.     
  401.     /*
  402.     call this from your main event loop after receiving and processing every
  403.     event. if the menus need updating, we send a message to the server asking
  404.     for our shared menus.
  405.     
  406.     if we load menus, they are assigned resource ids starting with idinsertafter.
  407.     this number must be less than 255 to allow for hierarchic menus, and must be
  408.     small enough so that no menu has an id of greater than 255. 
  409.     
  410.     9/28/91 DW: only update menus if we're the front process. this catches the
  411.     delay on re-loading a changed menu structure on the Multifinder switch. No
  412.     extra burden on the script writer editing the menu bar.
  413.     */
  414.         
  415.     if (!ProcessInForeground ()) /*only update menus if we're the front process*/
  416.         return (true);
  417.     
  418.     if (!MSglobals.fldirtysharedmenus) /*no need for an update, return quickly*/
  419.         return (true);
  420.         
  421.     DisposeSharedMenus ();
  422.     
  423.     if (ServerIsRunning ()) {
  424.     
  425.         if (GetSharedMenus (idinsertafter)) {
  426.     
  427.             InstallSharedMenus (0); /*install to the right of all other menus*/
  428.             
  429.             DrawMenuBar ();
  430.             }
  431.         
  432.         MSglobals.fldirtysharedmenus = false;
  433.         }
  434.         
  435.     else { /*server not running, menus have been updated (ie there are no shared menus)*/
  436.     
  437.         MSglobals.fldirtysharedmenus = false;
  438.         }
  439.         
  440.     return (true);
  441.     } /*CheckSharedMenus*/
  442.     
  443.     
  444. pascal Boolean SharedScriptRunning () {
  445.     
  446.     /*
  447.     returns true if a shared script is currently running, false otherwise.
  448.     
  449.     it's provided so that an application can intelligently handle cmd-period
  450.     script termination in its keystroke handling routine.
  451.     */
  452.     
  453.     return (MSglobals.flscriptrunning);
  454.     } /*SharedScriptRunning*/
  455.     
  456.  
  457. pascal Boolean CancelSharedScript () {
  458.     
  459.     /*
  460.     call this when the user presses cmd-period or otherwise indicates to you that
  461.     he or she wants the currently running script to be halted. 
  462.         
  463.     8/13/92 DW: if we're talking to post-2.0 Frontier or Runtime 1.0, we send a
  464.     message to the server telling it to kill the script. otherwise we do it the
  465.     old less elegant way, by setting a flag that gets monitored in calls to 
  466.     SharedScriptCancelled.
  467.     */
  468.     
  469.     AppleEvent event, reply;
  470.  
  471.     if (!MSglobals.flscriptrunning) /*nothing to do*/
  472.         return (true);
  473.         
  474.     if (!ServerSupportsFastMessages ()) {
  475.         
  476.         MSglobals.flscriptcancelled = true;
  477.         
  478.         return (true);
  479.         }
  480.   
  481.     if (!IACnewverb (MSglobals.serverid, MSglobals.serverid, 'kill', &event))
  482.         return (false);
  483.   
  484.     IACglobals.event = &event;
  485.   
  486.     if (!IACpushlongparam (MSglobals.idscript, '----'))
  487.         return (false);
  488.   
  489.     if (!IACsendverbnoreply (&event, &reply))
  490.         return (false);
  491.  
  492.     return (true);
  493.     } /*CancelSharedScript*/
  494.  
  495.  
  496. pascal Boolean SharedMenuHit (idmenu, iditem) short idmenu, iditem; {
  497.  
  498.     /*
  499.     returns true if the indicated menu and item indicate a shared menu item.
  500.     
  501.     if not, we return false -- the item is in one of your menus, you should
  502.     process the command as you normally would.
  503.     
  504.     we send an IAC message to the menu server, requesting that the script
  505.     linked into that item be run.
  506.     
  507.     we disable the shared menus, awaiting a 'done' message to re-enable them.
  508.     */
  509.  
  510.     if (!IsSharedMenu (idmenu)) /*not a shared menu*/
  511.         return (false);
  512.         
  513.     HiliteMenu (0);
  514.         
  515.     if (RunSharedMenuItem (idmenu, iditem)) {
  516.     
  517.         MSglobals.flscriptrunning = true;
  518.     
  519.         EnableSharedMenus (false);
  520.         }
  521.             
  522.     return (true);
  523.     } /*SharedMenuHit*/
  524.     
  525.  
  526. pascal Boolean SharedScriptCancelled (event, reply) AppleEvent *event, *reply;{
  527.     
  528.     /*
  529.     call this routine in each Apple event message handler that could conceivably 
  530.     be used in a script being run by the menu server. if we return false continue
  531.     processing the message as you normally would. if we return true, that means
  532.     that the script that's running has been cancelled by the user; you should 
  533.     return noErr from your Apple event handler when we return true.
  534.     
  535.     before we return true, we reply to the message on behalf of the message
  536.     handler. we send a specific error code of 6, this should be interpreted by
  537.     the scripting system as "stop running the script, but don't display an
  538.     error dialog.
  539.     
  540.     we admit this mechanism is somewhat klunky, but it proved too difficult to have
  541.     Frontier be ready to respond to a "Cancel Script" Apple event while running
  542.     the script and also giving time slices to agents.
  543.     
  544.     10/21/91 DW: thanks to Kevin Calhoun (Apple) we can tell who sent the message.
  545.     so we only reply with an error if the message arrived from the shared menu
  546.     server. it's nice to close this loop!
  547.     */
  548.  
  549.     Str255 s;
  550.     
  551.     if (MSglobals.flscriptcancelled && MSglobals.flscriptrunning) {
  552.         
  553.         IACglobals.event = event;
  554.         
  555.         if (IACgetsender () == MSglobals.serverid) { /*sender is shared menu server*/
  556.         
  557.             MSglobals.flscriptcancelled = MSglobals.flscriptrunning = false;
  558.             
  559.             s [0] = (char) 0; /*set length to 0*/
  560.             
  561.             IACglobals.reply = reply;
  562.         
  563.             IACreturnerror (6, s); /*server watches for this special error code*/
  564.             
  565.             return (true);
  566.             }
  567.         }
  568.         
  569.     return (false); /*script not cancelled, keep processing message*/
  570.     } /*SharedScriptCancelled*/
  571.     
  572.     
  573. static pascal OSErr HandleMenuDirty (event, reply, refcon) AppleEvent *event, *reply; long refcon; {
  574.     
  575.     /*
  576.     this Apple event handler is called when the application's menu bar has been 
  577.     edited by the script writer in the menu server's menu editor.
  578.     
  579.     we just record the dirty-ness of the menus in a boolean, we'll actually re-
  580.     load the menus when we become the foreground process.
  581.     */
  582.     
  583.     #pragma unused (event, reply, refcon)
  584.     
  585.     MSglobals.fldirtysharedmenus = true;
  586.     
  587.     return (noErr);
  588.     } /*HandleMenuDirty*/
  589.     
  590.     
  591. static pascal OSErr HandleScriptComplete (AppleEvent *event, AppleEvent *reply, long refcon) {
  592.     
  593.     /*
  594.     this Apple event handler is called when a menu script has completed running.
  595.     
  596.     we update a couple of menu-sharing globals and re-enable the shared menus.
  597.     
  598.     10/8/91 DW: added callback to support Applet Toolkit.
  599.     
  600.     1/22/92 DW: set IAC globals so callback routine can pop an error string from
  601.     the Apple Event. see appletmain.c for an example. if IACgetstringparam ('errs', s)
  602.     returns true, there was a runtime error in the script. you can display it in an alert 
  603.     dialog. syntax errors display in an error dialog in Frontier.
  604.     */
  605.     
  606.     MSglobals.flscriptcancelled = MSglobals.flscriptrunning = false;
  607.     
  608.     EnableSharedMenus (true);
  609.     
  610.     IACglobals.event = event; /*1/22/92 DW*/
  611.     
  612.     IACglobals.reply = reply;
  613.     
  614.     IACglobals.refcon = refcon;
  615.     
  616.     if (MSglobals.scriptcompletedcallback != nil)
  617.         (*MSglobals.scriptcompletedcallback) ();
  618.     
  619.     return (noErr);
  620.     } /*HandleScriptComplete*/
  621.     
  622.     
  623. pascal Boolean InitSharedMenus () {
  624.  
  625.     /*
  626.     sets the program up for menu sharing. we initialize the IAC Tools library and
  627.     then initialize the fields of MSglobals. 
  628.     
  629.     we install two Apple event message handlers -- one to catch the "menu needs update" 
  630.     message, and another to handle the "script has completed" message.
  631.     */
  632.     
  633.     if (!IACinit ()) /*Apple events aren't present, or otherwise couldn't init*/
  634.         return (false);
  635.     
  636.     MSglobals.serverid = 'LAND'; /*Frontier's creator id*/
  637.     
  638.     MSglobals.clientid = 0;
  639.         
  640.     MSglobals.hsharedmenus = nil; /*haven't loaded shared menus yet*/
  641.     
  642.     MSglobals.fldirtysharedmenus = true; /*force update 1st time thru event loop*/
  643.     
  644.     MSglobals.flscriptcancelled = false; /*script hasn't been cancelled*/
  645.     
  646.     MSglobals.flscriptrunning = false; /*no menu script running*/
  647.     
  648.     MSglobals.scriptcompletedcallback = nil;
  649.     
  650.     MSglobals.clientid = IACglobals.idprocess;
  651.     
  652.     if (!IACinstallhandler (MSglobals.clientid, 'updm', (ProcPtr) &HandleMenuDirty))
  653.         return (false);
  654.     
  655.     if (!IACinstallhandler (MSglobals.clientid, 'done', (ProcPtr) &HandleScriptComplete))
  656.         return (false);
  657.         
  658.     return (true);
  659.     } /*InitSharedMenus*/
  660.  
  661.  
  662.